`timescale 1ns / 1ps

`include "cp0.vh"

module cp0_reg(
	input wire clk,
	input wire rst,

	input wire we_i,
	input[4:0] waddr_i,
	input[4:0] raddr_i,
	input[31:0] data_i,
	
	input wire[5:0] int_i,

	input wire[31:0] excepttype_i,
	input wire[31:0] current_inst_addr_i,
	input wire is_in_delayslot_i,
	input wire[31:0] bad_addr_i,

output wire[31:0] count_o,
	output reg [31:0] data_o,
    output reg [31:0] compare_o,
	output reg [31:0] status_o,
	output reg [31:0] cause_o,
	output reg [31:0] epc_o,
	output reg [31:0] config_o,
	output reg [31:0] prid_o,
	output reg [31:0] badvaddr,
	output reg           timer_int_o
    );

	reg[32:0] count;
	assign count_o = count[32:1];
	
	always @(posedge clk) begin
		if(rst) begin
			count <= 0;
			compare_o <= 32'b0;
			status_o <= 32'b00010000000000000000000000000000;
			cause_o <= 32'b0;
			epc_o <= 32'b0;
			config_o <= 32'b00000000000000001000000000000000;
			prid_o <= 32'b00000000010011000000000100000010;
			timer_int_o <= 1'b0;
		end else begin
			count <= count + 1;
			cause_o[15:10] <= int_i;
			if(compare_o != 32'b0 && count_o == compare_o) begin
				/* code */
				timer_int_o <= 1'b1;
			end
			if(we_i) begin
				/* code */
				case (waddr_i)
					`CP0_REG_COUNT:begin 
						count[32:1] <= data_i;
					end
					`CP0_REG_COMPARE:begin 
						compare_o <= data_i;
						timer_int_o <= 1'b0;
					end
					`CP0_REG_STATUS:begin 
						status_o <= data_i;
					end
					`CP0_REG_CAUSE:begin 
						cause_o[9:8] <= data_i[9:8];
						cause_o[23] <= data_i[23];
						cause_o[22] <= data_i[22];
					end
					`CP0_REG_EPC:begin 
						epc_o <= data_i;
					end
					default : /* default */;
				endcase
			end
			case (excepttype_i)
				`CP0_EXC_INT:begin // 中断（其实写入的cause�?0�?
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_INT;
				end
				`CP0_EXC_ADEL:begin // 取指非对齐或Load非对�?
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_ADEL;
					badvaddr <= bad_addr_i;
				end
				`CP0_EXC_ADES:begin // Store非对�?
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_ADES;
					badvaddr <= bad_addr_i;
				end
				`CP0_EXC_SYS:begin // Syscall异常
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_SYS;
				end
				`CP0_EXC_BP:begin // BREAK异常
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_BP;
				end
				`CP0_EXC_RI:begin // 保留指令（译码失败）
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_RI;
				end
				`CP0_EXC_OV:begin // ALU溢出异常
					if(is_in_delayslot_i) begin
						/* code */
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1;
					end else begin 
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
					status_o[1] <= 1'b1;
					cause_o[6:2] <= `CP0_EXC_OV;
				end
				`CP0_EXC_ERET:begin // eret异常（准确说不叫异常，但通过这个在跳转到epc的同时清零status的EXL�?
					status_o[1] <= 1'b0;
				end
				default : /* default */;
			endcase
		end
	end

	always @(*) begin
		if(rst) begin
			/* code */
			data_o <= 32'b0;
		end else begin 
			case (raddr_i)
				`CP0_REG_COUNT:begin 
					data_o <= count_o;
				end
				`CP0_REG_COMPARE:begin 
					data_o <= compare_o;
				end
				`CP0_REG_STATUS:begin 
					data_o <= status_o;
				end
				`CP0_REG_CAUSE:begin 
					data_o <= cause_o;
				end
				`CP0_REG_EPC:begin 
					data_o <= epc_o;
				end
				`CP0_REG_PRID:begin 
					data_o <= prid_o;
				end
				`CP0_REG_CONFIG:begin 
					data_o <= config_o;
				end
				`CP0_REG_BADVADDR:begin 
					data_o <= badvaddr;
				end
				default : begin 
					data_o <= 32'b0;
				end
			endcase
		end
	
	end
endmodule
